import { fetchAndDecodeSubtree } from './SubtreeDecoder.js';
import { isContentAvailable, isTileAvailable, isChildSubtreeAvailable } from "./SubtreeDecoder";
const subtreeMap = new Map();
const QUADTREE_DEVISION_COUNT = 4;
const OCTREE_DEVISION_COUNT = 8;

const SUBDIVISION_COUNT_MAP = {
    QUADTREE: QUADTREE_DEVISION_COUNT,
    OCTREE: OCTREE_DEVISION_COUNT
};


async function resolveImplicite(rootTile, url) {
    if (!rootTile.root || !rootTile.root.implicitTiling) {
        return rootTile;
    }

    if (!rootTile.root.content && !rootTile.root.contents) {
        throw new Error("隐式瓦片需要一个Template URI");
    }

    let isQuad = true;
    if (rootTile.root.implicitTiling.subdivisionScheme) {
        isQuad = "QUADTREE" === rootTile.root.implicitTiling.subdivisionScheme.toUpperCase()
    }
    let subtreeUriTemplate = "";
    if (rootTile.root.implicitTiling.subtrees) {
        if (rootTile.root.implicitTiling.subtrees.uri) {
            subtreeUriTemplate = rootTile.root.implicitTiling.subtrees.uri;
        } else if (rootTile.root.implicitTiling.subtrees.url) {
            subtreeUriTemplate = rootTile.root.implicitTiling.subtrees.url;
        }
    }

    let contentURITemplates = [];
    if (rootTile.root.content) {
        if (rootTile.root.content.uri) {
            contentURITemplates.push(rootTile.root.content.uri);
        } else if (rootTile.root.content.url) {
            contentURITemplates.push(rootTile.root.content.url);
        }
    } else if (rootTile.root.contents) {
        rootTile.root.contents.forEach(content => {
            if (content.uri) {
                contentURITemplates.push(content.uri);
            } else if (content.url) {
                contentURITemplates.push(content.url);
            }
        })
    }
    const rootURL = getDirectoryUrl(url);

    let subtreeUri;
    if (isQuad) {
        subtreeUri = subtreeUriTemplate
            .replace("{level}", 0)
            .replace("{x}", 0)
            .replace("{y}", 0);
    }
    if (!isQuad) {
        subtreeUri = subtreeUriTemplate
            .replace("{level}", 0)
            .replace("{x}", 0)
            .replace("{y}", 0)
            .replace("{z}", 0);
    }

    const globalAddress = { level: 0, x: 0, y: 0 };
    const localAddress = { level: 0, x: 0, y: 0 };
    if (!isQuad) {
        globalAddress.z = 0;
        localAddress.z = 0;
    }
    const subtreeData = await fetchAndDecodeSubtree(rootURL + subtreeUri)
    subtreeMap.set(subtreeUri, subtreeData)
    const subtree = subtreeMap.get(subtreeUri);
    const contents = [];
    contentURITemplates.forEach((contentURI, index) => {
        if (isContentAvailable(localAddress, subtreeData, index)) {
            let uri;
            if (isQuad) {
                uri = contentURI
                    .replace("{level}", globalAddress.level)
                    .replace("{x}", globalAddress.x)
                    .replace("{y}", globalAddress.y);
            }
            if (!isQuad) {
                uri = contentURI
                    .replace("{level}", globalAddress.level)
                    .replace("{x}", globalAddress.x)
                    .replace("{y}", globalAddress.y)
                    .replace("{z}", globalAddress.z);
            }
            contents.push({ uri: uri });
        }
    });
    const explicitTileset = {
        geometricError: rootTile.root.geometricError,
        boundingVolume: rootTile.root.boundingVolume,
        refine: rootTile.root.refine,
        globalAddress: globalAddress,
        localAddress: localAddress,
        subtree: subtree,
        contents: contents,
        childIndex: 0,
        level: 0,
        parentData: {
            mortonIndex: 0,
            x: 0,
            y: 0,
            z: 0
        },
        globalData: {
            level: 0,
            mortonIndex: 0,
            x: 0,
            y: 0,
            z: 0
        },
        getChildren: async () => {
            return buildChildren(explicitTileset)
        }
    }
    return {
        root: explicitTileset

    }


    async function buildChildren(parent) {
        const children = [];
        if (parent.globalAddress.level >= rootTile.root.implicitTiling.availableLevels - 1) return children;
        if ((parent.globalAddress.level + 1) % rootTile.root.implicitTiling.subtreeLevels == 0) { // end of subtree 
            const globalAddresses = getChildrenAddresses(parent.globalAddress);
            const boundingVolumes = computeBoundingVolumes(isQuad, rootTile.root.boundingVolume, globalAddresses);

            for (let i = 0; i < globalAddresses.length; i++) {
                const childGlobalAddress = globalAddresses[i];
                let subtreeUri;
                if (isChildSubtreeAvailable(childGlobalAddress, parent.subtree)) {
                    if (isQuad) {
                        subtreeUri = subtreeUriTemplate
                            .replace("{level}", childGlobalAddress.level)
                            .replace("{x}", childGlobalAddress.x)
                            .replace("{y}", childGlobalAddress.y);
                    }
                    if (!isQuad) {
                        subtreeUri = subtreeUriTemplate
                            .replace("{level}", childGlobalAddress.level)
                            .replace("{x}", childGlobalAddress.x)
                            .replace("{y}", childGlobalAddress.y)
                            .replace("{z}", childGlobalAddress.z);
                    }
                }else{
                    continue;
                }
                if (!subtreeMap.has(subtreeUri)) {
                    const subtreeData = await fetchAndDecodeSubtree(rootURL + subtreeUri)
                    subtreeMap.set(subtreeUri, subtreeData)
                }
                const newSubtree = subtreeMap.get(subtreeUri)
                // if (!isTileAvailable(childGlobalAddress, newSubtree)) {
                //     continue;
                // }
                const newLocalAddress = { level: 0, x: 0, y: 0 };
                if (!isQuad) newLocalAddress.z = 0;

                const contents = [];
                contentURITemplates.forEach((contentURI, index) => {
                    const flag = isContentAvailable(newLocalAddress, newSubtree, index)
                    if (flag) {
                        let uri;
                        if (isQuad) {
                            uri = contentURI
                                .replace("{level}", childGlobalAddress.level)
                                .replace("{x}", childGlobalAddress.x)
                                .replace("{y}", childGlobalAddress.y);
                        }
                        if (!isQuad) {
                            uri = contentURI
                                .replace("{level}", childGlobalAddress.level)
                                .replace("{x}", childGlobalAddress.x)
                                .replace("{y}", childGlobalAddress.y)
                                .replace("{z}", childGlobalAddress.z);
                        }
                        contents.push({ uri: uri });
                    }

                });

                const child = {
                    geometricError: parent.geometricError / 2,
                    boundingVolume: boundingVolumes[i],
                    refine: rootTile.root.refine,
                    globalAddress: childGlobalAddress,
                    localAddress: newLocalAddress,
                    subtree: newSubtree,
                    contents: contents,
                    getChildren: async () => {
                        return buildChildren(child);
                    }
                }
                children.push(child);
            }
        } else { // not the end of the subtree
            const localAddresses = getChildrenAddresses(parent.localAddress);//subtree本地地址
            const globalAddresses = getChildrenAddresses(parent.globalAddress);//整个树的地址

            for (let i = 0; i < localAddresses.length; i++) {
                const childLocalAddress = localAddresses[i];
                const childGlobalAddress = globalAddresses[i];
                if (!isTileAvailable(childLocalAddress, parent.subtree)) {
                    continue;
                }
                const boundingVolume = computeBoundingVolume(isQuad, rootTile.root.boundingVolume, childGlobalAddress)
                const contents = [];
                for (let i = 0; i < contentURITemplates.length; i++) {
                    const flag = isContentAvailable(childLocalAddress, parent.subtree, i)
                    if (flag) {
                        const contentURITemplate = contentURITemplates[i];
                        let uri;
                        if (isQuad) {
                            uri = contentURITemplate
                                .replace("{level}", childGlobalAddress.level)
                                .replace("{x}", childGlobalAddress.x)
                                .replace("{y}", childGlobalAddress.y);
                        }
                        if (!isQuad) {
                            uri = contentURITemplate
                                .replace("{level}", childGlobalAddress.level)
                                .replace("{x}", childGlobalAddress.x)
                                .replace("{y}", childGlobalAddress.y)
                                .replace("{z}", childGlobalAddress.z);
                        }
                        console.log(uri);
                        contents.push({ uri: uri });
                    }
                }
                const child = {
                    geometricError: parent.geometricError / 2,
                    boundingVolume: boundingVolume,
                    refine: rootTile.root.refine,
                    globalAddress: childGlobalAddress,
                    localAddress: childLocalAddress,
                    subtree: parent.subtree,
                    contents: contents,
                    getChildren: async () => {
                        return buildChildren(child);
                    }
                }
                children.push(child)
            }
        }
        return children.length > 0 ? children : undefined;
    }
}

function getChildrenAddresses(tileAddress) {
    const { level, x, y, z } = tileAddress;
    const nextLevel = level + 1;
    let children = [];

    if (z === undefined) {
        // Quadtree
        children = [
            { level: nextLevel, x: x * 2, y: y * 2 }, // Bottom left
            { level: nextLevel, x: x * 2 + 1, y: y * 2 }, // Bottom right
            { level: nextLevel, x: x * 2, y: y * 2 + 1 }, // Top left
            { level: nextLevel, x: x * 2 + 1, y: y * 2 + 1 }  // Top right
        ];
    } else {
        // Octree
        children = [
            { level: nextLevel, x: x * 2, y: y * 2, z: z * 2 },     // 左下正面
            { level: nextLevel, x: x * 2 + 1, y: y * 2, z: z * 2 },     // 右下正面
            { level: nextLevel, x: x * 2, y: y * 2 + 1, z: z * 2 },     // 左下后
            { level: nextLevel, x: x * 2 + 1, y: y * 2 + 1, z: z * 2 },     // 右下后
            { level: nextLevel, x: x * 2, y: y * 2, z: z * 2 + 1 }, // 上前左
            { level: nextLevel, x: x * 2 + 1, y: y * 2, z: z * 2 + 1 }, // 上前右
            { level: nextLevel, x: x * 2, y: y * 2 + 1, z: z * 2 + 1 }, // 上后左
            { level: nextLevel, x: x * 2 + 1, y: y * 2 + 1, z: z * 2 + 1 }  // 上后右
        ];
    }

    return children;
}
function getAddresses(tileAddress) {
    let { level, x, y, z } = tileAddress;
    level=0
    let children = [];
    if (z === undefined) {
        // Quadtree
        children = [
            { level: level, x: x * 2, y: y * 2 }, // Bottom left
            { level: level, x: x * 2 + 1, y: y * 2 }, // Bottom right
            { level: level, x: x * 2, y: y * 2 + 1 }, // Top left
            { level: level, x: x * 2 + 1, y: y * 2 + 1 }  // Top right
        ];
    } else {
        // Octree
        children = [
            { level: level, x: x * 2, y: y * 2, z: z * 2 },     // Bottom front left
            { level: level, x: x * 2 + 1, y: y * 2, z: z * 2 },     // Bottom front right
            { level: level, x: x * 2, y: y * 2 + 1, z: z * 2 },     // Bottom back left
            { level: level, x: x * 2 + 1, y: y * 2 + 1, z: z * 2 },     // Bottom back right
            { level: level, x: x * 2, y: y * 2, z: z * 2 + 1 }, // Top front left
            { level: level, x: x * 2 + 1, y: y * 2, z: z * 2 + 1 }, // Top front right
            { level: level, x: x * 2, y: y * 2 + 1, z: z * 2 + 1 }, // Top back left
            { level: level, x: x * 2 + 1, y: y * 2 + 1, z: z * 2 + 1 }  // Top back right
        ];
    }

    return children;
}

function getDirectoryUrl(url) {
    const cleanUrl = url.split('?')[0]; // Removes query parameters
    return cleanUrl.substring(0, cleanUrl.lastIndexOf('/') + 1);
};

function computeBoundingVolumes(isQuad, rootBoundingVolume, addresses) {
    const boundingVolumes = [];
    for (let i = 0; i < addresses.length; i++) {
        boundingVolumes.push(computeBoundingVolume(isQuad, rootBoundingVolume, addresses[i]))
    }
    return boundingVolumes;
}

function computeBoundingVolume(isQuad, rootBoundingVolume, address) {
    if (rootBoundingVolume.region) {
        return splitRegion(isQuad, rootBoundingVolume.region, address);
    } else if (rootBoundingVolume.box) {
        return splitBox(isQuad, rootBoundingVolume.box, address);
    } else {
        throw new Error('不支持 bounding volume type');
    }
}

function splitRegion(isQuad, rootRegion, address) {
    // 解构rooregion
    const [west, south, east, north, minHeight, maxHeight] = rootRegion;

    // 根据tile的等级计算每个分割步长的大小
    const xStep = (east - west) / (2 ** address.level);
    const yStep = (north - south) / (2 ** address.level);
    const zStep = isQuad ? 0 : (maxHeight - minHeight) / (2 ** address.level);

    // 计算tile的新边界
    const newWest = west + (xStep * address.x);
    const newSouth = south + (yStep * address.y);
    const newEast = newWest + xStep;
    const newNorth = newSouth + yStep;

    // 对于四叉树，z维保持不变。对于八叉树，计算新的高度
    const newMinHeight = isQuad ? minHeight : minHeight + (zStep * address.z);
    const newMaxHeight = isQuad ? maxHeight : newMinHeight + zStep;

    return { region: [newWest, newSouth, newEast, newNorth, newMinHeight, newMaxHeight] };
}

function splitBox(isQuad, box, address) {
    // 从box中提取中心和半长向量
    const center = box.slice(0, 3);
    const vectors = [
        box.slice(3, 6),  // First 半长向量
        box.slice(6, 9),  // Second 半长向量
        box.slice(9, 12)  // Third 半长向量
    ];

    // 计算每个轴的缩放比例
    const s = 1 / Math.pow(2, address.level);
    const scaleFactors = [s, s, isQuad ? 1 : s];

    // 根据缩放比例调整半长向量
    const newVectors = vectors.map((vector, index) => vector.map(component => component * scaleFactors[index]));

    const origin = [center[0] - vectors[0][0] - vectors[1][0] - vectors[2][0], center[1] - vectors[0][1] - vectors[1][1] - vectors[2][1], center[2] - vectors[0][2] - vectors[1][2] - vectors[2][2]]
    // 根据子位置计算新中心
    const newCenter = [
        origin[0] + (address.x * 2 + 1) * (newVectors[0][0] + newVectors[1][0] + newVectors[2][0]),
        origin[1] + (address.y * 2 + 1) * (newVectors[0][1] + newVectors[1][1] + newVectors[2][1]),
        isQuad ? center[2] : origin[2] + (address.z * 2 + 1) * (newVectors[0][2] + newVectors[1][2] + newVectors[2][2]),
    ];

    // 用更新后的中心和缩放后的半长向量构造新的box
    const newBox = newCenter.concat(...newVectors);

    return { box: newBox };
}

function concatBits(first, second) {
    return parseInt(first.toString(2) + second.toString(2), 2);
}


export {
    resolveImplicite
}
